package com.itheima.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.bean.Category;
import com.itheima.bean.Dish;
import com.itheima.bean.DishFlavor;
import com.itheima.bean.PageParam;
import com.itheima.dto.DishDto;
import com.itheima.mapper.CategoryMapper;
import com.itheima.mapper.DishFlavorMapper;
import com.itheima.mapper.DishMapper;
import com.itheima.service.DishService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Transactional
@Service
public class DishServiceImpl implements DishService {
    @Autowired
    private DishMapper dm;

    @Autowired
    private DishFlavorMapper dfm;

    @Autowired
    private CategoryMapper cm;

    //注入RedisTemplate
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 根据分类id来查询一下菜品表里面的总记录数
     * @param cid
     * @return
     */
    @Override
    public long findCountByCategoryId(long cid) {
        LambdaQueryWrapper<Dish> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Dish::getCategoryId , cid);
        //select count(*) from dish where category_id = cid
        return dm.selectCount(lqw);
    }

    /**
     * 新增菜品
     * @param dto 包含了菜品的基本信息，还包含了口味信息
     * @return
     */
    @Override
    public int add(DishDto dto) {
        //1、先往菜品表里面添加数据
        int row = dm.insert(dto);

        int count = 0;

        //2、再往菜品口味表里面添加数据
        //只有当菜品数据添加成功了之后，才去做菜品口味表的添加
        if (row > 0){
            //也不能盲目的就去遍历口味数据，还要判定有没有口味数据，如果有就遍历，然后添加到数据库
            List<DishFlavor> flavorList = dto.getFlavors();
            if (flavorList != null && flavorList.size() > 0){
                for (DishFlavor flavor : dto.getFlavors()) {
                    //设置这个口味是哪个菜品的
                    flavor.setDishId(dto.getId());
                    count += dfm.insert(flavor);
                }
            }
        }
        //添加好菜品之后，要修改Redis里面的数据。把当前这种菜品分类的数据给删除掉
        String key = "dish_"+dto.getCategoryId()+"_1";
        redisTemplate.delete(key);

        //只有菜品表添加成功了 并且口味表也添加成功了，才算是真的成功，最后返回1即表示成功，否则表示失败
        return (row > 0 && count == dto.getFlavors().size()) ? 1 : 0;
    }

    /**
     * 分页查询菜品数据
     *      1、这里的分页比较页数一些了，不能像之前的员工的分页一样简单
     *      2、以前的员工分页只要查询员工表(employee)即可，然后到页面上显示，数据也是足够的
     *      3、但是现在的菜品的分页数据，除了拥有菜品本身的数据之外，还需要有菜品的分类名称！
     *          3.1 现在关键点就是菜品的分类名称在菜品表里面没有体现，只有菜品的分类id
     *          3.2 需要得到分页的菜品数据之后，遍历每一个菜品，拿着菜品的分类id去查询分类表
     *              获取到分类的名称。
     *      4、这里还要考虑一件事情，就是：
     *          4.1 如果我们去查询dish表，执行分页查询，返回的结果是：IPage<Dish>
     *          4.2 上一层(Controller) 需要的是IPage<DishDto>
     *          4.3 要自己构造这个IPage<DishDto>,把dish分页的数据(集合数据和总记录数)封装到
     *              DishDto里面，除此之外，还要封装分类的名称进去。
     * @param pageParam
     * @return
     */
    @Override
    public IPage<DishDto> findPage(PageParam pageParam) {
        //1、对dish表做分页查询
        //1.1 设置查询第几页 每页查询多少条
        IPage<Dish> p = new Page<>(pageParam.getPage(),pageParam.getPageSize());
        //1.2 判定查询的条件有没有，如果有，就追加查询条件
        LambdaQueryWrapper<Dish> lqw = new LambdaQueryWrapper<>();
        lqw.like(pageParam.getName() != null,Dish::getName,pageParam.getName());
        //1.3 执行分页查询
        IPage<Dish> dishIPage = dm.selectPage(p,lqw);

        //-----------------分析-----------------
        //上面经过查询，已经得到了菜品的分页数据，但是这些数据里面，每隔菜品的分类名称没有。
        //所以现在要遍历每一个菜品，然后去查询分类表，得到分类的名称。

        //2、构建IPage<DishDto> 对象，然后往里面自己填充值
        //  页面对于这个IPage只要两个数据：一个是：total(总记录数)，一个是records(集合数据)
        IPage<DishDto> dishDtoIPage  = new Page<>();
        //2.1 封装总记录数
        dishDtoIPage.setTotal(dishIPage.getTotal());
        //2.2 构建集合数据
        ArrayList<DishDto> dtoList = new ArrayList<>();


        //3、对分类表进行查询
        for (Dish dish : dishIPage.getRecords()) {
            //3.1 得到菜品的分类id
            Long categoryId = dish.getCategoryId();
            //3.2 拿着分类的id去查询分类的数据库
            Category category = cm.selectById(categoryId);
            //3.3 每遍历一个dish对象，我们就自己创建一个DishDto对象与之相应
            DishDto dto = new DishDto();
            //3.4 封装分类名称
            dto.setCategoryName(category.getName());
            //3.5 封装dish的基本数据
            BeanUtils.copyProperties(dish,dto);

            //现在还需要去查询这个菜品的口味有哪些，这个查询得到的口味数据并不会在分页的列表上显示
            //只是为了后面的修改数据回显做准备
            LambdaQueryWrapper<DishFlavor> fq = new LambdaQueryWrapper<>();
            fq.eq(DishFlavor::getDishId,dish.getId());
            List<DishFlavor> flavorList = dfm.selectList(fq);
            //把这个口味的集合撞到dto里面去
            dto.setFlavors(flavorList);
            //3.6 封装到集合去
            dtoList.add(dto);
        }
        //最后把这个dtoList集合装到IPage里面去
        dishDtoIPage.setRecords(dtoList);
        return dishDtoIPage;
    }

    /**
     * 修改菜品
     * @param dto 包含了菜品的基本信息，还包含了口味信息
     * @return
     */
    @Override
    public int update(DishDto dto) {
        //1、先修改菜品的基本数据
        Dish dish = new Dish();
        //从dto的属性拷贝到dish这个对象里面来
        BeanUtils.copyProperties(dto,dish);

        int row = dm.updateById(dish);//这里只能放Dish，怎么办呢
        int count = 0;

        if (row > 0){
            //2、再修改口味的数据
            //2.1 把这个菜品的所有口味数据删除掉
            LambdaQueryWrapper<DishFlavor> fq = new LambdaQueryWrapper<>();
            fq.eq(DishFlavor::getDishId,dish.getId());
            dfm.delete(fq);

            //2.2 再把现在这个页面传递过来的口味数据添加到口味表里面
            List<DishFlavor> flavorList = dto.getFlavors();
            if (flavorList != null && flavorList.size() > 0){
                for (DishFlavor flavor : dto.getFlavors()) {
                    //设置这个口味是哪个菜品的
                    flavor.setDishId(dto.getId());

                    //手动设置id
                    flavor.setId(IdWorker.getId());

                    //执行口味数据的添加
                    count += dfm.insert(flavor);
                }
            }
        }
        //修改mysql成功之后，要把Redis里面所有的菜品分类数据全部删除掉
        //找到前缀名称为 dish_ 这一类的KEY
        Set keys = redisTemplate.keys("dish_*");
        redisTemplate.delete(keys);


        return (row > 0 && count == dto.getFlavors().size()) ? 1 : 0;
    }

    /**
     * 根据菜品的分类id来查询菜品数据
     * @param categoryId
     * @return
     */
    @Override
    public List<DishDto> findByCategoryId(Long categoryId,int status) {

        /*
            设置Redis里面操作的KEY
                1. 前面多加了dish_ 主要是为了让我们以后查看Redis数据的时候，可以容易识别这份缓存数据是什么类型的缓存数据
                2. 后面的_1加不加都可以，，它加上主要是为了表示这种类型的数据，是状态为1(起售)的数据
         */
        String key = "dish_"+categoryId+"_1";

        //0、不用先去查询mysql数据库了，上来先去查询Redis，里面有还是没有数据库
        //   如果有就直接返回 ， 没有就要去查询mysql数据库
        String data = (String) redisTemplate.opsForValue().get(key);

        if (data != null){//如果从Redis里面取到的数据不为空，表示有数据
            return JSON.parseObject(data,List.class);
        }


        //1、定义条件
        LambdaQueryWrapper<Dish> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Dish::getCategoryId,categoryId);
        //判断这个菜品是否属于在售菜品
        lqw.eq(Dish::getStatus,status);

        //2、查询菜品的数据集合
        List<Dish> dishList = dm.selectList(lqw);
        //3、手动封装Dto集合
        List<DishDto> dtoList = new ArrayList<>();
        //4、遍历菜品的集合
        for (Dish dish : dishList) {
            //4.1 构建一个DishDto对象
            DishDto dishDto = new DishDto();
            //4.2 拷贝基本信息
            BeanUtils.copyProperties(dish,dishDto);
            //4.3 要封装口味数据.要去查询菜品口味表，得到这个菜品它的口味数据
            LambdaQueryWrapper<DishFlavor> lqw2 = new LambdaQueryWrapper<>();
            lqw2.eq(DishFlavor::getDishId,dish.getId());
            List<DishFlavor> dishFlavors = dfm.selectList(lqw2);

            dishDto.setFlavors(dishFlavors);
            //4.4 把这个dto撞到list集合里面
            dtoList.add(dishDto);

            //5、把mysql查询出来的数据，保存到redis里面去，以便下一次来调用方法的时候，Redis里面就有数据了

            //redisTemplate.opsForList().leftPush("dish_"+categoryId+"_1", JSON.toJSONString(dtoList));
            redisTemplate.opsForValue().set(key, JSON.toJSONString(dtoList));
        }



        return dtoList;
    }
}
